package org.tlang.interpreter;

import org.tlang.metaclass.TNativeFunction;
import org.tlang.context.Context;
import org.tlang.exception.EvalException;
import org.tlang.lexer.Lexer;
import org.tlang.lib.NativeFunctions;
import org.tlang.parser.Parser;
import org.tlang.parser.SegmentParser;
import org.tlang.type.*;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class SegmentInterpreter extends AbsInterpreter {

    @Override
    protected Parser createParser(Lexer lexer) {
        return new SegmentParser(lexer);
    }

    @Override
    protected Context createGlobalContext() {
        Context context = new Context();
        presetNativeFunctions(context);
        return context;
    }

    private void presetNativeFunctions(Context context) {
        appendMethod(context, NativeFunctions.class, "println", void.class, String.class);
        appendMethod(context, NativeFunctions.class, "currentTimeMillis", int.class);
        appendMethod(context, NativeFunctions.class, "nanoTime", int.class);
        appendMethod(context, NativeFunctions.class, "intToStr", String.class, long.class);
        appendMethod(context, NativeFunctions.class, "boolToStr", String.class, boolean.class);
        appendMethod(context, NativeFunctions.class, "floatToStr", String.class, double.class);
    }

    private void appendMethod(Context context, Class<?> clazz, String methodName, Class<?> ret, Class<?>... params) {
        try {
            Method method = clazz.getMethod(methodName, params);
            int index = context.symbolTable().add(methodName);
            context.typeTable().put(index, new FuncType(methodName, transToType(ret), transToTypes(params)));
            context.valueTable().add(index, new TNativeFunction(method, methodName));
        } catch (NoSuchMethodException e) {
            throw new EvalException("not find a native function: " + methodName);
        }
    }

    private Type[] transToTypes(Class<?>... clazzList) {
        List<Type> result = new ArrayList<>();
        for (Class<?> clazz : clazzList) {
            result.add(transToType(clazz));
        }
        return result.toArray(new Type[0]);
    }

    private Type transToType(Class<?> clazz) {
        if (clazz == String.class) {
            return StrType.STR_TYPE;
        } else if (clazz == long.class) {
            return IntType.INT_TYPE;
        } else if (clazz == boolean.class) {
            return BoolType.BOOL_TYPE;
        } else if (clazz == void.class) {
            return VoidType.VOID_TYPE;
        } else if (clazz == double.class) {
            return FloatType.FLOAT_TYPE;
        }
        return VoidType.VOID_TYPE;
    }
}
